﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates;
using Microsoft.VisualStudio.TextTemplating;

namespace Blog.Joubin.Ca
{
    public class CodeGenerator
    {
        public void Process(Dictionary<string, string> parms)
        {
            if (!parms.ContainsKey("OUTPUTDIR") || !Directory.Exists(parms["OUTPUTDIR"]))
                throw new ArgumentException(string.Format("Output directory argument (OutputDir) either is not provided or doesn't exist."));

            if (!parms.ContainsKey("DESIGNPATTERN") || !Enum.IsDefined(typeof(DesignPatternType), parms["DESIGNPATTERN"].ToUpper()))
                throw new ArgumentException(string.Format("Design pattern argument (DesignPattern) either is not provided or not supported."));

            if (!parms.ContainsKey("LANGUAGE") || !Enum.IsDefined(typeof(LanguageType), parms["LANGUAGE"].ToUpper()))
                throw new ArgumentException(string.Format("Language argument (Language) either is not provided or not supported."));

            if (!parms.ContainsKey("NAMESPACE"))
                throw new ArgumentException(string.Format("Namespace argument (Namespace) is not provided."));

            if (!parms.ContainsKey("CLASSNAME"))
                throw new ArgumentException(string.Format("Class name argument (Classname) is not provided."));

            string ext = parms["LANGUAGE"].ToUpper() == "CS" ? "cs" : "vb";

            DirectoryInfo dinfo = Directory.GetParent(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location));
            dinfo = Directory.GetParent(dinfo.FullName);
            string templatesFolder = string.Format("{0}\\{1}\\{2}", dinfo.FullName, parms["DESIGNPATTERN"], ext);

            string[] t4s = Directory.GetFiles(templatesFolder);
            IEnumerable<string> t4Files =
                            from name in t4s
                            where name.EndsWith(".tt") || name.EndsWith(".TT")
                            select name;
            if (t4Files.Count() == 0)
            {
                Console.WriteLine("There were no T4 templates found.");
                return;
            }
            Console.WriteLine("A total of {0} template files were found...\n", t4Files.Count());

            // Prepare template parameters
            var arguments = new Dictionary<string, PropertyData>();
            foreach (KeyValuePair<string, string> kvp in parms)
                arguments.Add(kvp.Key, new PropertyData(kvp.Value, typeof(string)));
            arguments.Add("ASSEMBLYNAME", new PropertyData(System.Reflection.Assembly.GetExecutingAssembly().GetName().Name, typeof(string)));

            // Generate code using T4 templates
            foreach (string t4File in t4Files)
            {
                string currentDirectory = Directory.GetCurrentDirectory();
                TemplateHost host = new TemplateHost(parms["OUTPUTDIR"], arguments);
                host.TemplateFile = Path.Combine(currentDirectory, t4File);

                // Transform template
                string template = File.ReadAllText(host.TemplateFile);
                ITextTemplatingEngine engine = new Engine();
                string output = engine.ProcessTemplate(template, host);

                // Save output
                string outputfilename = Path.GetFileName(host.TemplateFile);
                string outputfilenameChanged = string.Empty;

                DesignPatternType designPattern = (DesignPatternType)Enum.Parse(typeof(DesignPatternType), parms["DESIGNPATTERN"].ToUpper());
                if (designPattern == DesignPatternType.PROVIDERMODEL)
                {
                    if (outputfilename == "Config.tt")
                        outputfilenameChanged = "Configuration.config";
                    else if (outputfilename.ToUpper().EndsWith("concrete.tt", StringComparison.CurrentCultureIgnoreCase))
                        outputfilenameChanged = string.Format("FirstDataProvider.{0}", ext);
                    else
                        outputfilenameChanged = outputfilename.Replace("My", parms["CLASSNAME"]).Replace("tt", ext);
                }
                else if (designPattern == DesignPatternType.SINGLETON)
                {
                    outputfilenameChanged = string.Format("{0}.{1}", parms["CLASSNAME"], ext);
                }
                string outputFilePath = string.Format("{0}\\{1}", parms["OUTPUTDIR"], outputfilenameChanged);
                File.WriteAllText(outputFilePath, output.Trim());

                Console.WriteLine("Generated {0}", outputFilePath);
                
                foreach (CompilerError error in host.Errors)
                {
                    Console.WriteLine(error.ToString());
                }
            }
        }
    }

    /// <summary>
    /// Supported design patterns
    /// </summary>
    public enum DesignPatternType
    {
        PROVIDERMODEL,
        SINGLETON
    }

    /// <summary>
    /// Supported programming languages
    /// </summary>
    public enum LanguageType
    {
        CS,
        VB
    }
}
